What is tiny-lr?
The tiny-lr package is a lightweight LiveReload server for use during development. It allows developers to automatically refresh the browser when files are changed, improving the development workflow by providing immediate feedback.
What are tiny-lr's main functionalities?
Start a LiveReload server
This feature allows you to start a LiveReload server on a specified port (35729 in this case). The server listens for changes and notifies the browser to reload.
const tinylr = require('tiny-lr');
const server = tinylr();
server.listen(35729, function() {
console.log('... Listening on %s ...', 35729);
});
Trigger a reload
This feature allows you to manually trigger a reload by notifying the server of file changes. In this example, the server is notified that 'index.html' has changed, prompting a reload in the browser.
const tinylr = require('tiny-lr');
const server = tinylr();
server.listen(35729, function() {
console.log('... Listening on %s ...', 35729);
});
// Simulate a file change
server.changed({ body: { files: ['index.html'] } });
Other packages similar to tiny-lr
livereload
The livereload package provides similar functionality to tiny-lr by enabling automatic browser refreshes when files change. It is more feature-rich and supports a wider range of use cases, including integration with various build tools and task runners.
browser-sync
Browser-sync is a powerful tool for synchronizing file changes, interactions, and scroll positions across multiple devices. It offers more advanced features compared to tiny-lr, such as UI for controlling the server, and is often used in more complex development environments.
tiny-lr
This script manages a tiny LiveReload server
implementation.
It exposes an HTTP server and express middleware, with a very basic REST
Api to notify the server of a particular change.
It doesn't have any watch ability, it must be done at the build process or
application level.
Instead, it exposes a very simple API to notify the server that some
changes have been made, then broadcasted to every livereload client
connected.
# notify a single change
curl http://localhost:35729/changed?files=style.css
# notify using a longer path
curl http://localhost:35729/changed?files=js/app.js
# notify multiple changes, comma or space delimited
curl http://localhost:35729/changed?files=index.html,style.css,docs/docco.css
Or you can bulk the information into a POST request, with body as a JSON array of files.
curl -X POST http://localhost:35729/changed -d '{ "files": ["style.css", "app.js"] }'
# from a JSON file
node -pe 'JSON.stringify({ files: ["some.css", "files.css"] })' > files.json
curl -X POST -d @files.json http://localhost:35729
As for the livereload client, you need to install the browser extension:
http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-
(note: you need to listen on port 35729 to be able to use with your
brower extension)
or add the livereload script tag manually:
http://feedback.livereload.com/knowledgebase/articles/86180-how-do-i-add-the-script-tag-manually-
(and here you can choose whatever port you want)
Integration
The best way to integrate the runner in your workflow is to add it as a reload
step within your build tool.
var tinylr = require('tiny-lr');
var port = 35729;
tinylr().listen(port, function() {
console.log('... Listening on %s ...', port);
})
You can define your own route and listen for specific request:
var server = tinylr();
server.on('GET /myplace', function(req, res) {
res.write('Mine');
res.end();
})
And stop the server manually:
server.close();
This will close any websocket connection established and emit a close event.
Middleware
To use as a connect / express middleware, tiny-lr needs query /
bodyParser middlewares prior in the stack (to handle POST requests)
Any handled requests ends at the tinylr level, not found and errors are
nexted to the rest of the stack.
var port = process.env.LR_PORT || process.env.PORT || 35729;
var path = require('path');
var express = require('express');
var tinylr = require('tiny-lr');
var body = require('body-parser');
var app = express();
app
.use(body())
.use(tinylr.middleware({ app: app }))
.use(express.static(path.resolve('./')))
.listen(port, function() {
console.log('listening on %d', port);
});
The port you listen on is important, and tinylr should always listen on
the LiveReload standard one: 35729
. Otherwise, you won't be able to rely
on the browser extensions, though you can still use the manual snippet
approach.
You can also start two different servers, one on your app port, the
other listening on the LiveReload port.
Using grunt
Head over to https://github.com/gruntjs/grunt-contrib-watch
Using make
See make-livereload repo.
This repository defines a bin wrapper you can use and install with:
npm install make-livereload -g
It bundles the same bin wrapper previously used in tiny-lr repo.
Usage: tiny-lr [options]
Options:
-h, --help output usage information
-V, --version output the version number
port -p
pid Path to the generated PID file (default: ./tiny-lr.pid)
Using gulp
See gulp-livereload repo.
Options
livereload
- Path to the client side lib (defaults to path.join(__dirname, '../node_modules/livereload-js/dist/livereload.js')
)port
- Livereload port (defaults to 35729
)errorListener
- A callback to invoke when an error occurs (otherwise, fallbacks to standard error output)app
- An express or other middleware based HTTP serverkey
- Option to pass in to create an https servercert
- Option to pass in to create an https serverpfx
- Can also be used to create an https server instead of key
& cert
liveCSS
- LiveReload option to enable live CSS reloading (defaults to true)liveJs
- LiveReload option to enable live JS reloading (defaults to true)liveImg
- LiveReload option to enable live images reloading (defaults to true)
Tests
npm test
TOC
# tiny-lr
accepts ws clients.
var url = parse(this.request.url);
var server = this.app;
var ws = this.ws = new WebSocket('ws://' + url.host + '/livereload');
ws.onopen = function(event) {
var hello = {
command: 'hello',
protocols: ['http://livereload.com/protocols/official-7']
};
ws.send(JSON.stringify(hello));
};
ws.onmessage = function(event) {
assert.deepEqual(event.data, JSON.stringify({
command: 'hello',
protocols: ['http://livereload.com/protocols/official-7'],
serverName: 'tiny-lr'
}));
assert.ok(Object.keys(server.clients).length);
done();
};
properly cleans up established connection on exit.
var ws = this.ws;
ws.onclose = done.bind(null, null);
request(this.server)
.get('/kill')
.expect(200, function() {
console.log('server shutdown');
});
# tiny-lr
## GET /
respond with nothing, but respond.
request(this.server)
.get('/')
.expect('Content-Type', /json/)
.expect('{"tinylr":"Welcome","version":"0.0.1"}')
.expect(200, done);
unknown route respond with proper 404 and error message.
request(this.server)
.get('/whatev')
.expect('Content-Type', /json/)
.expect('{"error":"not_found","reason":"no such route"}')
.expect(404, done);
## GET /changed
with no clients, no files.
request(this.server)
.get('/changed')
.expect('Content-Type', /json/)
.expect(/"clients":\[\]/)
.expect(/"files":\[\]/)
.expect(200, done);
with no clients, some files.
request(this.server)
.get('/changed?files=gonna.css,test.css,it.css')
.expect('Content-Type', /json/)
.expect('{"clients":[],"files":["gonna.css","test.css","it.css"]}')
.expect(200, done);
## POST /changed
with no clients, no files.
request(this.server)
.post('/changed')
.expect('Content-Type', /json/)
.expect(/"clients":\[\]/)
.expect(/"files":\[\]/)
.expect(200, done);
with no clients, some files.
var data = { clients: [], files: ['cat.css', 'sed.css', 'ack.js'] };
request(this.server)
.post('/changed')
.send({ files: data.files })
.expect('Content-Type', /json/)
.expect(JSON.stringify(data))
.expect(200, done);
## GET /livereload.js
respond with livereload script.
request(this.server)
.get('/livereload.js')
.expect(/LiveReload/)
.expect(200, done);
## GET /kill
shutdown the server.
var server = this.server;
request(server)
.get('/kill')
.expect(200, function(err) {
if(err) return done(err);
assert.ok(!server._handle);
done();
});
Thanks!